/*
 * Object.c
 *
 *  Created on: 2011-7-8
 *      Author: gexiao
 */
/*
 * Operations on an Object.
 */
#include "../Dalvik.h"

/*
 * Find a matching field, in the current class only.
 *
 * Returns NULL if the field can't be found. (Does not throw an exception.)
 */
InstField* dvmFindInstanceField(const ClassObject* clazz,
		const char* fieldName, const char* signature) {
	InstField* pField;
	int i;

	assert(clazz!=NULL);

	/*
	 * Find a field with a matching name and signature. The Java programming
	 * language does not allow you to have two fields with the same name
	 * and different types, but the Java VM spec does allow it, so we can't
	 * bail out early when the name matches.
	 */
	pField = clazz->ifields;
	for (i = 0; i < clazz->ifieldCount; i++, pField++) {
		if (strcmp(fieldName, pField->field.name) == 0 && strcmp(signature,
				pField->field.signature) == 0) {
			return pField;
		}
	}

	return NULL;
}

/*
 *
 */
StaticField* dvmFindStaticField(const ClassObject* clazz,
    const char* fieldName, const char* signature) {

}
/*
 * Compare the given name, return type, and argument types with the contents
 * of the given method. This returns 0 if they are equal and non-zero if not.
 */
static inline int compareMethodHelper(Method* method, const char* methodName,
		const char* returnType, size_t argCount, const char** argTypes) {
	DexParameterIterator iterator;
	const DexProto* proto;

	if (strcmp(methodName, method->name) != 0) {
		return 1;
	}

	proto = &method->prototype;

	if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) {
		return 1;
	}

	if (dexProtoGetParameterCount(proto) != argCount) {
		return 1;
	}

	dexParameterIteratorInit(&iterator, proto);

	for (; argCount != 0; argCount--, argTypes++) {
		const char* argType = *argTypes;
		const char* paramType = dexParameterIteratorNextDescriptor(&iterator);

		if (paramType == NULL) {
			//Param list ended early; no much
			break;
		} else if (strcmp(argType, paramType) != 0) {
			//Types aren't the same; no match.
			break;
		}
	}

	if (argCount == 0) {
		//We ran through all the given arguments...
		if (dexParameterIteratorNextDescriptor(&iterator) == NULL) {
			//and through all the method's arguments; success!
			return 0;
		}
	}

	return 1;
}

/*
 * Get the count of arguments in the given method descriptor string,
 * and also find a pointer to the return type.
 */
static inline size_t countArgsAndFindReturnType(const char* descriptor,
		const char** pReturnType) {
	size_t count = 0;
	bool bogus = false;
	bool done = false;

	assert(*descriptor=='(');
	descriptor++;

	while (!done) {
		switch (*descriptor) {
		case 'B':
		case 'C':
		case 'D':
		case 'F':
		case 'I':
		case 'J':
		case 'S':
		case 'Z': {
			count++;
			break;
		}

		case '[': {
			do {
				descriptor++;
			} while (*descriptor == '[');
			/*
			 * Don't increment count, as it will be taken care of
			 * by the next iteration. Also, decrement descriptor
			 * to compensate for the increment below the switch.
			 */
			descriptor--;
			break;
		}

		case 'L': {
			do {
				descriptor++;
			} while ((*descriptor != ';') && (*descriptor != '\0'));
			count++;
			if (*descriptor == '\0') {
				//Bogus descriptor.
				done = true;
				bogus = true;
			}
			break;
		}

		case ')': {
			/*
			 * Note: The loop will exit after incrementing descriptor
			 * one more time, so it then points at the return type.
			 */
			done = true;
			break;
		}

		default: {
			//Bogus descriptor.
			done = true;
			bogus = true;
			break;
		}
		}
		descriptor++;
	}

	if (bogus) {
		*pReturnType = NULL;
		return 0;
	}

	*pReturnType = descriptor;
	return count;
}

/*
 * Copy the argument types into the given array using the given buffer
 * for the contents.
 */
static inline void copyTypes(char* buffer, const char** argTypes,
		size_t argCount, const char* descriptor) {
	size_t i;
	char c;

	//Skip the '('.
	descriptor++;

	for (i = 0; i < argCount; i++) {
		argTypes[i] = buffer;

		//Copy all the array markers and one extra character.
		do {
			c = *(descriptor++);
			*(buffer++) = c;
		} while (c == '[');

		if (c == 'L') {
			//Copy the rest of a class name.
			do {
				c = *(descriptor++);
				*(buffer++) = c;
			} while (c != ';');
		}
		*(buffer++) = '\0';
	}
}

/*
 * Look for a match in the given class. Returns the match if found
 * or NULL if not.
 */
static Method* findMethodInListByDescriptor(const ClassObject* clazz,
		bool findVirtual, bool isHier, const char* name, const char* descriptor) {
	const char* returnType;
	size_t argCount = countArgsAndFindReturnType(descriptor, &returnType);

	if (returnType == NULL) {
		return NULL;
	}

	/*
	 * Make buffer big enough for all the argument type characters and
	 * one '\0' per argument. The "-2" is because "returnType-
	 * descriptor" includes two parens.
	 */
	char buffer[argCount + (returnType - descriptor) - 2];
	const char* argTypes[argCount];

	copyTypes(buffer, argTypes, argCount, descriptor);

	while (clazz != NULL) {
		Method* methods;
		size_t methodCount;
		size_t i;

		if (findVirtual) {
			methods = clazz->virtualMethods;
			methodCount = clazz->virtualMethodCount;
		} else {
			methods = clazz->directMethods;
			methodCount = clazz->directMethodCount;
		}

		for (i = 0; i < methodCount; i++) {
			Method* method = &methods[i];
			if (compareMethodHelper(method, name, returnType, argCount,
					argTypes) == 0) {
				return method;
			}
		}

		if (!isHier) {
			break;
		}
		clazz = clazz->super;
	}

	return NULL;
}

/*
 *
 */
Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
	    const char* methodName, const char* descriptor) {

}

/*
 *
 */
Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
    const char* methodName, const char* descriptor) {

}

/*
 * Find a "direct" method (static,private,or "<init>").
 *
 * Returns NULL if the method can't be found. (Does not throw an exception.)
 */
Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
		const char* methodName, const char* descriptor) {
	return findMethodInListByDescriptor(clazz, false, false, methodName,
			descriptor);
}

/*
 * Get the source file for a method.
 */
const char* dvmGetMethodSourceFile(const Method* meth) {
	/*
	 * TODO: A method's debug info can override the default source
	 * file for a class, so we should account for that possibility
	 * here.
	 */
	return meth->clazz->sourceFile;
}
